package com.tool.grab.vpn.tunnel;




import android.net.VpnService;

import com.tool.grab.Constant;
import com.tool.grab.vpn.listener.DeleteEvent;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.concurrent.ConcurrentLinkedQueue;

public class TcpTunnel implements TunnelInterface{
    private final static String TAG = TcpTunnel.class.getSimpleName();

    private VpnService service;

    /**
     * 套接字通道
     */
    private SocketChannel socketChannel;

    /**
     * 数据缓存
     */
    private Selector selector;

    /**
     * 是否是请求
     */
    private boolean isRequest;

    private int sum;

    /**
     * 服务通道
     */
    private TunnelInterface brotherTunnel;

    private InetSocketAddress address;

    private ConcurrentLinkedQueue<ByteBuffer> needWriteData = new ConcurrentLinkedQueue<>();
    private DeleteEvent event;

    public TcpTunnel(SocketChannel socketChannel, Selector selector, DeleteEvent event) throws IOException{
        this.event = event;
        this.socketChannel = socketChannel;
        this.selector = selector;
        this.isRequest = true;

        Socket socket = socketChannel.socket();
        if(socket == null) throw new IOException("创建套接字通道失败");

        this.address = new InetSocketAddress(socket.getLocalAddress(),socket.getLocalPort());
        InetSocketAddress socketAddress = new InetSocketAddress(socket.getInetAddress(),socket.getPort());

        this.brotherTunnel = new TcpTunnel(socketAddress,selector);
        this.brotherTunnel.bindTunnel(this);

        if(socketChannel.isConnected()) socketChannel.finishConnect();
    }


    private TcpTunnel(InetSocketAddress address, Selector selector) throws IOException{
        this.address = address;
        this.selector = selector;

        this.socketChannel = SocketChannel.open();
        this.socketChannel.configureBlocking(false);
    }

    @Override
    public void connect(VpnService service) throws IOException {
        this.service = service;

        if(service == null || this.socketChannel == null || this.selector == null || this.address == null){
            throw new IOException("VPN连接异常");
        }

        if(service.protect(this.socketChannel.socket())){
            this.socketChannel.register(this.selector,SelectionKey.OP_CONNECT | SelectionKey.OP_WRITE |SelectionKey.OP_READ,this);
            this.socketChannel.connect(this.address);
        }
    }

    /**
     * 连接键通信
     * @param key
     * @throws IOException
     */
    @Override
    public void keyReady(SelectionKey key) throws IOException{
        if (key.isReadable()) {
            onReader();
        } else if (key.isWritable()) {
            onWriter();
        } else if (key.isConnectable()) {
            if(this.isRequest){
                this.brotherTunnel.connect(this.service);
            }
        }
    }

    /**
     * 读取数据
     * @throws IOException
     */
    private void onReader() throws IOException{
        ByteBuffer buffer = ByteBuffer.allocate(Constant.MUTE_SIZE);
        buffer.clear();
        int sum = this.socketChannel.read(buffer);

        if(sum > 0){
            buffer.flip();

            if(this.brotherTunnel != null){
                this.brotherTunnel.sendPacket(buffer);
            }
        }
    }


    /**
     * 写入数据
     * @throws IOException
     */
    private void onWriter()throws IOException{
        ByteBuffer buffer = this.needWriteData.poll();

        if(buffer == null) return;

        while (buffer.hasRemaining()){

            int sendSum = this.socketChannel.write(buffer);

            this.sum += sendSum;

            if(sendSum == 0) break;
        }
    }

    /**
     * 收到重对端发过来的数据包
     * @param buffer
     */
    @Override
    public void sendPacket(ByteBuffer buffer) {
        if(buffer.hasRemaining()){
            if(this.needWriteData.size() == 0){
                int sum = 0;

                try {
                    while (buffer.hasRemaining()){
                        int b = this.socketChannel.write(buffer);
                        sum += b;
                        if( b == 0) break;
                    }
                } catch (IOException e) {
                    sum = 0;
                }

                if(sum > 0) return;
            }

            this.needWriteData.offer(buffer);
            this.selector.wakeup();
        }
    }

    /**
     * 关闭套接字通道
     */
    @Override
    public void onClose() {

        if(this.event != null) this.event.onDelete(this);

        if(isOpen()){
            try {
               this.socketChannel.close();
            }catch (IOException e) {
            }finally {
                this.socketChannel = null;
            }
        }

        if(this.brotherTunnel != null && this.brotherTunnel.isOpen()){
            this.brotherTunnel.onClose();
        }
    }


    /**
     * 套接字是否打开
     * @return
     */
    @Override
    public boolean isOpen() {
        return this.socketChannel != null;
    }

    /**
     * 是否连接
     * @return
     */
    public boolean isConnect(){
        return this.socketChannel != null && this.socketChannel.isConnected();
    }

    /**
     * 是否是客户端请求
     * @return
     */
    public boolean isRequest() {
        return this.isRequest;
    }

    /**
     * 绑定兄弟通道
     * @param tunnel
     */
    @Override
    public void bindTunnel(TunnelInterface tunnel) {
        this.brotherTunnel = tunnel;
    }

}
